"""
使用说明：
按此编写
def demo(request):
    # 只支持FK组件："level"  "creator"
    group = SearchGroup(models.Customer, request,
                        Option('mobile', is_multi=False, is_choice=False, db_condition={'id__lte': 11},
                               text_func=lambda x: x.mobile, value_func=lambda x: x.mobile),
                        Option('level', is_multi=False, db_condition={'active': 1}),
                        Option('creator', is_multi=True, ),
                        Option('gender', is_multi=False, is_choice=True, text_func=lambda x: str(x[1]),
                               value_func=lambda x: str(x[0])),
                        Option('hbs', is_multi=True)
                        )
    row_list = group.get_row_list()
    condition = group.get_condition()
    queryset = models.Customer.objects.filter(**condition).order_by('-id')
    # pager = Pagination(request, queryset)
    return render(request, 'v2.html', {"queryset": queryset, "row_list": row_list})


    在HTML中
    <link rel="stylesheet" href="{% static 'css/search-group.css' %}">
    {% include 'include/search_group.html' %}
"""
from django.db.models import ForeignKey,ManyToManyField

class Row():
    def __init__(self, opt_obj, title, queryset_or_list, request):
        self.opt_obj = opt_obj
        self.title = title
        self.queryset_or_list = queryset_or_list
        self.query_dict = request.GET

    def __iter__(self):
        # self.opt_obj.is_multi
        query_dict = self.query_dict.copy()
        loop_query_dict = self.query_dict.copy()
        query_dict._mutable = True
        loop_query_dict._mutable = True
        yield '<div class="whole">'
        yield self.title
        yield '</div>'
        yield '<div class="other">'
        value_list = query_dict.getlist(self.opt_obj.field)
        if value_list:
            query_dict.pop(self.opt_obj.field)
            yield f'<a href="?{query_dict.urlencode()}">全部</a>'
        else:
            yield f'<a class="active" href="?{query_dict.urlencode()}">全部</a>'
        for obj in self.queryset_or_list:
            # if self.opt_obj.is_choice:
            #     text = obj[1]
            #     value = str(obj[0])
            # else:
            #     text = str(obj)
            #     value = str(obj.pk)
            # text = self.opt_obj.text_func(obj)
            # value = self.opt_obj.value_func(obj)
            text = self.opt_obj.get_text(obj)
            value = self.opt_obj.get_value(obj)
            if self.opt_obj.is_multi:
                # loop_query_dict._mutable = True
                multi_list = loop_query_dict.getlist(self.opt_obj.field)
                if (value in multi_list):
                    multi_list.remove(value)
                    query_dict.setlist(self.opt_obj.field, multi_list)
                    url = query_dict.urlencode()
                    yield f"<a class='active' href='?{url}'>{text}</a>"
                else:
                    multi_list.append(value)
                    query_dict.setlist(self.opt_obj.field, multi_list)
                    url = query_dict.urlencode()
                    yield f"<a href='?{url}'>{text}</a>"
            else:
                # loop_query_dict._mutable = True
                value_list = loop_query_dict.getlist(self.opt_obj.field)
                query_dict.setlist(self.opt_obj.field, [value])

                url = query_dict.urlencode()
                if value in value_list:
                    yield f"<a class='active' href='?{url}'>{text}</a>"
                else:
                    yield f"<a href='?{url}'>{text}</a>"
        yield '</div>'


class SearchGroup(object):
    def __init__(self, model_class, request, *options):
        self.model_class = model_class
        self.request = request
        self.options = options

    def get_row_list(self):
        row_list = []
        for opt_obj in self.options:
            field_object = self.model_class._meta.get_field(opt_obj.field)
            # (field_object.choices)
            title = field_object.verbose_name
            if opt_obj.is_choice:
                queryset_or_list = field_object.choices
            else:
                if isinstance(field_object, ForeignKey) or isinstance(field_object, ManyToManyField):
                    queryset_or_list = opt_obj.get_queryset(field_object.related_model, self.request)
                else:
                    #         获取当前表中的数据
                    queryset_or_list = self.model_class.objects.filter(**opt_obj.db_condition)
            obj = Row(opt_obj, title, queryset_or_list, self.request)
            row_list.append(obj)
        return row_list

    def get_condition(self):
        condition = {}
        for opt_obj in self.options:
            field, value = opt_obj.get_search_condition(self.request)
            if not value:
                continue
            condition[field] = value
        return condition


class Option(object):
    def __init__(self, field, is_multi=False, db_condition=None, is_choice=False, text_func=None, value_func=None):
        self.field = field
        self.is_multi = is_multi
        if not db_condition:
            db_condition = {}
        self.db_condition = db_condition
        self.is_choice = is_choice
        self.text_func = text_func
        self.value_func = value_func

    def get_search_condition(self, request):
        if self.is_multi:
            value = request.GET.getlist(self.field)
            return f"{self.field}__in", value
        else:
            value = request.GET.get(self.field)
            return f"{self.field}", value

    def get_queryset(self, related_model, request):
        return related_model.objects.filter(**self.db_condition)

    def get_text(self, obj_or_tuple):
        if self.text_func:
            return self.text_func(obj_or_tuple)
        if self.is_choice:
            return obj_or_tuple[1]
        return str(obj_or_tuple)

    def get_value(self, obj_or_tuple):
        if self.value_func:
            return self.value_func(obj_or_tuple)
        if self.is_choice:
            return str(obj_or_tuple[0])
        return str(obj_or_tuple.pk)